package com.googlecode.jwsm;

import java.util.jar.*;
import java.util.zip.*;
import java.util.*;
import java.io.*;
import java.net.*;

import org.jcommon.io.jar.*;
 
public class JarClassLoader extends ClassLoader {
	private HashMap classes;
	private HashMap resourceURL;
	private File[] files;
 
	public JarClassLoader(File[] files, ClassLoader parent) {
		super(parent);
		this.files = files;
		classes = new HashMap();
		resourceURL = new HashMap();
	}
 
	public Class loadClass(String className) throws ClassNotFoundException {
		return (loadClass(className, true));
	}
 
	public synchronized Class loadClass(String className, boolean resolve) throws ClassNotFoundException {
		if (classes.containsKey(className)) return (Class)classes.get(className);
		ZipFile zipFile = null;
		BufferedInputStream bis = null;
		byte[] res = null;
		ZipEntry zipEntry = null;
		try {
			for (int i = 0; i < files.length; i++) {
				zipFile = new ZipFile(files[i]);
				zipEntry = zipFile.getEntry(className.replace('.', '/') + ".class");
				if (zipEntry != null) break;
				try {
					zipFile.close();
				} catch(IOException exc2) {}
			}
			res = new byte[(int)zipEntry.getSize()];
			bis = new BufferedInputStream(zipFile.getInputStream(zipEntry));
			bis.read(res, 0, res.length);
		} catch (Exception ex) {
		} finally {
			if (bis!=null) {
				try {
					bis.close();
				} catch (IOException ioex) {}
			}
			if (zipFile!=null) {
				try {
					zipFile.close();
				} catch (IOException ioex) {}
			}
		}
		
		if (res == null) {
			//return super.findSystemClass(className);
			Class c = getParent().loadClass(className);
			return c;
		}
 
		Class clazz = defineClass(className, res, 0, res.length);
		if (clazz == null) throw new ClassFormatError();
 
		if (resolve) resolveClass(clazz);
		classes.put(className, clazz);
		return(clazz);
	}
	
	protected URL findResource(String name) {
		if (resourceURL.containsKey(name)) {
			URL url = (URL)resourceURL.get(name);
			if (url != null) return url;
		}
		
		URL url = super.findResource(name);
		if (url == null) {
			ZipFile zipFile = null;
			ZipEntry zipEntry;
			for (int i = 0; i < files.length; i++) {
				try {
					zipFile = new ZipFile(files[i]);
					zipEntry = zipFile.getEntry(name);
					if (zipEntry != null) {
						url = new URL(null, "jar:file:/" + files[i].getCanonicalPath().replace('\\', '/') + "!/" + name, new JARStreamHandler());
						break;
					}
				} catch(Exception exc) {
				} finally {
					if (zipFile != null) {
						try {
							zipFile.close();
						} catch(IOException exc2) {}
					}
				}
			}
		}
		if (url != null) {
			resourceURL.put(name, url);
		}
		return url;
	}

	public static final String[] dependencyDuplicationCheck(File jarFile, ClassLoader loader) throws ZipException, IOException {
		JarFile jar = new JarFile(jarFile);
		ArrayList list = new ArrayList();
		Enumeration e = jar.entries();
		while (e.hasMoreElements()) {
			JarEntry entry = (JarEntry)e.nextElement();
			if (entry.getName().endsWith(".class")) {
				try {
					String name = entry.getName().replace('/', '.');
					name = name.substring(0, name.length() - ".class".length());
					loader.loadClass(name);
					list.add(name);
				} catch(ClassNotFoundException exc) {
				}
			}
		}
		return (String[])list.toArray(new String[list.size()]);
	}
	
	public static final int getClassCount(File jarFile) throws IOException {
		JarFile jar = new JarFile(jarFile);
		Enumeration e = jar.entries();
		int count = 0;
		while (e.hasMoreElements()) {
			JarEntry entry = (JarEntry)e.nextElement();
			if (entry.getName().endsWith(".class")) {
				count++;
			}
		}
		return count;
	}
}